package lock

import (
	"errors"
	"fmt"
	"io/ioutil"
	"strings"
	"time"

	"github.com/go-redis/redis"
)

const (
	MinExpireTime = 500 * time.Millisecond //最小生命周期
)

type Mutex interface {
	Lock() (bool, error)
	Unlock() error
	GetNodeID() string
}
type redisMutex struct {
	isRenewal   bool                          //是否执行续期
	isClose     bool                          //是否已经关闭
	cacheKey    string                        //缓存key
	nodeId      string                        //实例id
	expire      time.Duration                 //生命周期
	timer       *time.Timer                   //续期定时器
	conn        func() (*redis.Client, error) //连接方法
	stopTimerCh chan struct{}                 //通知通道
	//mu          sync.Mutex
}

/**
 * @description: 获取redis锁
 * @param {string} cacheKey，缓存key
 * @param {time.Duration} expire，生命周期
 * @param {bool} isRenewal，是否执行续期
 * @return {*}
 */
func GetRedisMutex(cacheKey string, expire time.Duration, isRenewal bool) (mu Mutex, err error) {
	if expire < MinExpireTime {
		err = errors.New("最小生命周期为500ms")
		return
	}
	if cacheKey == "" {
		err = errors.New("cacheKey为空")
		return
	}
	mu = &redisMutex{
		cacheKey:  cacheKey,
		nodeId:    GetServerHostName(),
		expire:    expire,
		isRenewal: isRenewal,
		conn: func() (redisClient *redis.Client, err error) {
			options := &redis.Options{
				Addr: "127.0.0.1:6379",
			}
			redisClient = redis.NewClient(options)
			return
		},
	}
	return
}
func GetServerHostName() string {
	var serverHostName string
	contents, err := ioutil.ReadFile("/etc/hostname")
	if err == nil {
		fmt.Println(err.Error())
		return serverHostName
	}
	serverHostName = strings.Replace(string(contents), "\n", "", 1)
	return serverHostName
}

/**
 * 加锁
 */
func (r *redisMutex) Lock() (ok bool, err error) {
	client, err := r.conn()
	if err != nil {
		return
	}
	ok, err = client.SetNX(r.cacheKey, r.nodeId, r.expire).Result()
	if err != nil {
		return
	}
	r.isClose = false
	if r.isRenewal && ok {
		go r.doRenewal()
	}
	return
}

/**
 * 执行续期
 */
func (r *redisMutex) doRenewal() {
	renwalTime := r.expire >> 1
	r.timer = time.NewTimer(renwalTime)
	r.stopTimerCh = make(chan struct{})
	defer func() {
		ok := r.timer.Stop()
		if ok {
			r.isClose = true
		}
	}()
	for {
		select {
		case <-r.timer.C: //执行续期
			client, err := r.conn()
			if err != nil {
				return
			}
			ok, err := client.PExpire(r.cacheKey, r.expire).Result()
			if err != nil {
				r.stopTimerCh <- struct{}{}
				return
			}
			if !ok {
				r.stopTimerCh <- struct{}{}
			}
			r.timer.Reset(renwalTime)
		case <-r.stopTimerCh: //停止
			return
		}
	}
}

/**
 * 解锁
 */
func (r *redisMutex) Unlock() (err error) {
	client, err := r.conn()
	if err != nil {
		return
	}
	if r.isClose {
		return
	}
	_, err = client.Del(r.cacheKey).Result()
	if err != nil {
		return
	}
	r.stopTimerCh <- struct{}{}
	return
}

/**
 * 获取当前nodeId
 */
func (r *redisMutex) GetNodeID() string {
	return r.nodeId
}
